import axios1 from 'axios'
import to from '@/utils/awaitTo' // 把to函数请过来-让它包装住axios请求, 把结果处理成数组再返回到业务页面中
import store from '@/store'
import Notify from '@/ui/Notify'
import router from '@/router'
// import { removeStorage } from '@/storage'
import { refreshTokenAPI } from '@/api'

// axios.defaults.baseURL = 'http://toutiao-app.itheima.net' // 请求的基础路径

// import jsonBig from 'json-bigint'
var JSONbigString = require('json-bigint')({ storeAsString: true })
// 在axios中增加配置项
const axios = axios1.create({
  baseURL: 'http://toutiao-app.itheima.net',
  // 处理响应数据
  transformResponse: [function (data) { // data是前台请求获取的后台返回的JSON字符串
    // 对 data 进行任意转换处理
    // axios默认用JSON.parse将数据转成json格式的了，
    // 这里不用JSON.parse因为它处理不了大数
    // jsonBig.parse来转换JSON字符串, 遇到大数会转成BigNumber类型
    // 目标: 它默认把大数转成BigNumber
    // 默认把大数以字符串的形式放在数据对象里
    // 204响应(DELETE删除成功的响应状态码，当data里无数据，就不用了JSON转换)
    if (data.length !== 0) {
      return JSONbigString.parse(data)
    } else {
      return data
    }
  }]
})

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
  if (store.state.token) { // vuex有token再添加Authorization头
    // 在发送之前 统一添加Authorization头 -- 携带身份标识
    config.headers.Authorization = 'Bearer ' + store.state.token // 就可以删除其他页面发请求写的请求头，这里配置了，就不用了再配置了
  }
  // console.log(config) // config 就是发送的请求内容
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

// 目标：无登录也可以去首页
// 判断有token才携带(有token的拿的数据是这个用户对应的首页频道数据)
// 必须让后台配合
// 反例：如果后台要求你频道必须传token,必须登录后才能看首页
// 正例：你不传token，后台默认给你返回7个频道，免登录看首页

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
  // 对响应数据做点什么
  // console.log(response) // response就是响应对象 正确的结果
  return response
}, async function (error) { // 报错4xx/5xx进入这里
  // console.dir(error) // 错误的响应 内容
  if (error?.response?.status === 401) { // 401 token未传, 值不对,过期了
    // removeStorage('token') // 本地token清空
    // store.commit('clearToken') // 清空vuex中的token值

    // token过期处理方式1 -- 跳转到登录页，用户重新登录
    // Notify({ type: 'danger', message: '网络请求错误-token过期' })
    // setTimeout(() => {
    //   router.push({ name: 'Login' }) // 回登录页
    // }, 500) // 给用户500ms反应

    // token过期处理方式2：用户无感知 自动重新请求新的token  (token续签)
    // const [, res] = await refreshTokenAPI()
    // console.log(res)
    // store.commit('setToken', res.data.data.token) // 把新的token保存到本地和vuex中
    // // 虽然你把新的token拿回来了, 但是上一次请求时是401, 成功了吗?
    // // 再无感知重新请求一遍(但是不知道用户请求的地址是什么)
    // // 重要: 把headers的Authorization的值换成新的正确的, 再发送刚才报401的请求一次
    // console.log(2)
    // console.dir(error) // 有用户当前地址和要去的地址和headers等信息，发现没有Authorization字段，因为vuex中没token了，所以请求拦截器中设置的Authorization没执行
    // error.config.headers.Authorization = res.data.data.token // 手动添加Authorization，然后再重新发请求
    // return axios(error.config) // 返出去 发送一次请求

    // 优化
    if (store.state.refresh_token === null) { // 证明没有refresh_token, 用户没登陆过
      Notify({ type: 'danger', message: '请登录' })
      setTimeout(() => {
        router.push({ name: 'Login' }) // 回登录页
      }, 500)
    } else { // 有refresh_token
      const [, res] = await refreshTokenAPI() // 用refresh_token重新从后台获取token值
      if (error?.response?.status === 403) { // refresh_token过期或者错误
        Notify({ type: 'danger', message: '请重新登录' })
        setTimeout(() => {
          router.push({ name: 'Login' }) // 回登录页
        }, 500)
      } else { // refresh_token正常, 拿到正常的新的token
        // console.log(res)
        store.commit('setToken', res.data.data.token) // 把新的token保存到本地和vuex中
        // 虽然你把新的token拿回来了, 但是上一次请求时是401, 成功了吗?
        // 再无感知重新请求一遍
        // console.log(error) // 里面没有请求头
        // 重要: 把headers的Authorization的值换成新的正确的, 再发送刚才报401的请求一次
        error.config.headers.Authorization = res.data.data.token // 把新的token带上
        return axios(error.config) // 发送一次未完成的请求
      }
    }
  }
  return Promise.reject(error)
})

export default ({ url, method = 'GET', data = {}, headers = {}, params = {} }) => {
  // const PromiseA = axios({
  //   url,
  //   method,
  //   data,
  //   headers,
  //   params
  // })
  // return to(PromiseA)
  // 简单写法
  return to(axios({
    url,
    method,
    data,
    headers,
    params
  }))
}

// 以后如果换网络请求的库, 直接更换这个文件里的代码, 保证代码的复用性和独立性(高内聚低耦合)

// 假如替换了请求库为 $.ajax请求
// export default (url, method = 'GET', data = {}, headers = {}, params = {}) => {
//   return new Promise((resolve, reject) => {
//     // 如果有params写js原生代码, 把params对象里的值, 拼接到url后面去
//     $.ajax({
//       url,
//       type: method,
//       data,
//       headers,
//       success (res) {
//         resolve(res)
//       },
//       error (err) {
//         reject(err)
//       }
//     })
//   })
// }

// 在App.vue中使用的示例代码
// const [, res] = await loginAPI({ // ,占位,我要接收数组的第二位的值解构出来, loginAPI返回的是[错误对象, 成功对象]
//   mobile: '13900000000',
//   code: '246810'
// }) // PromiseB-then里返回一个数组
// console.log(res)

// 登录方式4种:
// 1. 登录状态保存到数据库
// 问题: 分布式(服务器+数据库集群) - n多台代码+数据库一样的服务器
// (1): 你登录状态保存到了服务器A里面
// (2): 再次请求服务器A比较忙, 服务器B来做出你的响应(没有你的登录状态, 判定你未登录)
// 分布式: 同步状态(性能开销更大)
// 无状态:可以用分布式

// 2. session (类似于数据库-有别于数据库)
// session后端文件保存状态(小数据), 方式
// 登录的状态, 保存到session里
// 如果遇到分布式, session也需要同步

// 3. token (json web token)
// 好: 服务器端不保存用户登录的状态, 相同的一份解析和判定的代码(分布式n台服务器一样的代码)
// (1): 登录在服务器A上, 服务器A返回了一套token值(保存在前端)
// (2): 再次请求服务器B应答的, 服务器B拿到前端发来的token(用相同一套代码)验证, 结果一样
// 疑问: 前端保存不怕被破解吗?
// 解答: token组成:
// header / payload / sign
// {"typ":"JWT","alg":"HS256"}
// 反过来解密
// 签名(加密的一串字符串)
// 对前面2者, 进行一个加密转换(秘钥只保存在服务器上)
// 服务器端每次接到请求以后, 都会把前2部分加密后和sign对比.
// 问题: 如果前端也加密. 把sign的值也给你串改
// 但是你不要忘记, 后端是用自己的秘钥把前2部分加密和sing对比 (还是通不过)
// 比较安全: 只要后端不把秘钥暴露出来, 前端是无法破译的

// 4. 登录方式: outh登录
// 微信登录/qq登录/微博登录/苹果登录/....
// 需要集成对应平台的开发者账号
// 微信开放平台: https://mp.weixin.qq.com/?token=&lang=zh_CN
// 不支持个人集成, 都需要有公司资质
